package gameLoop;

import java.util.Vector;

import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.opengl.Display;

import physics.Physics;


import render.Render;
import render.RenderChunk;
import save.Save;
import world.Noise;
import world.World;

import engineUtil.Clock;
import engineUtil.InputHandler;
import engineUtil.SimpleClock;
import entity.Cube;
import entity.Player;

/*
 * Author: William Stern
 * Class: CS680
 * 
 * Loop based on c++ code in slides from week 2.
 */

public class GameEngine {
	
	
	private SimpleClock gameClock;

	private Clock realClock;
	private boolean directRender= false;
	private boolean vboRender = false;
	private boolean displaylistRender=false;
	private Physics physics;
	//private Render render;
	private RenderChunk renChunk= new RenderChunk();
	private World world;
	private Save save;
	private Player player= new Player("player",6.0f,2.0f,1.0f); 
	
	private Cube cubeFriend = new Cube("cube",1.0f,1.0f,1.0f);
	
	private InputHandler io= new InputHandler(player);

	private boolean vboWithIndices=false;
	private Render renderManger = new Render();
	/*
	 * States
	 * 
	 * splash
	 * 
	 * menu
	 * 
	 * world select
	 * 
	 * game
	 * 
	 * quit
	 * 
	 * pause
	 * 
	 * 
	 */
	
	//private static double maxDt=0.25;
	
public GameEngine()
{
	
}//

public void initializeClocks()
{

gameClock = new SimpleClock();// initialize a new clock to track game time

}//

public void initializeGameEngine()
{
	
	
	//render = new Render();
	
	world = new World();
	
	save = new Save();
	//player.
	player.setPos(new float[]{-10.0f,-10.0f,-10.0f});
	cubeFriend.setPos(new float[]{10.0f,10.0f,10.0f});
	
	//player.setPos(new float[]{0.0f,100.0f,0.0f});
}


public void itterateGameState(double dt)
{
	//method to update the game state
	
	
	//world.getManager().manageChunks(player);
	
	physicsUpdate(dt);
	
	collisionsUpdate(dt);
	
	
	updateStatistics();
	
	
		
	
}//end updateGameState

private void collisionsUpdate(double dt)
{

}//method to handle collisions

private void physicsUpdate(double dt)
{


	
	//physics.removeBody(player);
	//physics.addBody(player);
	//physics.removeBody(cubeFriend);
	//physics.addBody(cubeFriend);	
	
physics.update((float)dt);
	//method to handle physics and collisions interactions

//update player and cubeFriend
//player=(Player) physics.getPhysicsBodies().elementAt(0);
//cubeFriend=(Cube) physics.getPhysicsBodies().elementAt(1);
}

private void updateStatistics()
{

//method to handle statistics information
}

private void render(float dt)
{

//render.renderACube();
	
	//System.err.print("RenderList is empty: "+world.getManager().isRenderListEmpty());
	
//	if(!world.getManager().isRenderListEmpty())
//	{
			
			//renChunk.setRenderList(world.getManager().getRenderList());
			renChunk.setPlayer(player);
			
			//renChunk.getPlayer().setPos(player.getPos());
			//renChunk.getPlayer().setRot(player.getRot());
			//renChunk.setRenderList(world.getManager().getRenderableChunks());
			
			
			if(directRender)
			{
//			renChunk.updateVBO();
			renChunk.renderImediate(dt);
			}
			
			if(vboRender)
			{
			//renChunk.createVBOData();
//			renderManger.render();
				//renChunk.clear();
				//renChunk.transformations();
			renChunk.renderVBO();
			//renChunk.popMatrix();
				//
			}
			
			if(vboWithIndices)
			{
				renChunk.renderVBOWithIndices();	
			}
	//will handle rendering the game frame
} 
private void pollForHCI(float dt)
{
	//System.err.println("dt: "+dt);
io.setPlayer(player);
io.keyboardHandler(dt);
io.mouseHandler(dt);
player = io.getPlayer();
//player.setPos(io.getPlayer().getPos());

//player.setRot(io.getPlayer().getRot());

//represents the component which will take user input.
}

public SimpleClock getGameClock() {
	return gameClock;
}

public void setGameClock(SimpleClock gameClock) {
	this.gameClock = gameClock;
}

/*public Render getRender() {
	return render;
}

public void setRender(Render render) {
	this.render = render;
}*/

public World getWorld() {
	return world;
}

public void setWorld(World world) {
	this.world = world;
}

public Save getSave() {
	return save;
}

public void setSave(Save save) {
	this.save = save;
}

public Clock getRealClock() {
	return realClock;
}

public void setRealClock(Clock realClock) {
	this.realClock = realClock;
}

/*public void singleStep()
{
	if(gameClock.isPaused())
	{
		
	gameClock.singleStep();//update the time
	itterateGameState(gameClock.getDtSeconds());//run one frame of the game at the ideal frame rate 
	//gameClock.setTimeBegin();
	}
	
	

}*/

public  void run() throws LWJGLException
{
	// initialization
	
	//directRender=true;
	vboRender=true;
	//vboWithIndices=true;
	//double dtSeconds =1.0/30.0;// assume an idea dt of 30 fps
	int MAX_FRAME_SKIP = 10;
	int REDRAWING_PERIOD =20;
	boolean need_to_redraw = true;
	
	//GameEngine engine = new GameEngine();
	initializeClocks();//initalize game clocks
	initializeGameEngine();
	//initialize game engine
	
	
	//generate a new window	
	/*
	render.createWindow();
	render.setUpShader();
	render.setUpTexture();
	
	render.buildVBO();
	*/
	
	
	
	//renChunk.setUpChunk();
	//initialize open gl
	
	
	world.setWorldSize( new int[] {6,1,6});
	
	/*
	 * The noise modifier entered changes the kind of terrain generated
	 */
	
	world.setNoiseModifer(new double[]{0.15,0.05});
	
	//world.setNoiseModifer(new double[]{0.30,0.09});
	//world.setNoiseModifer(new double[]{0.30,0.39});
	//world.setNoiseModifer(new double[]{0.30,0.19});
	
	//world.setNoiseModifer(new double[]{0.60,0.09});
	
	//world.setNoiseModifer(new double[]{0.15,0.15});
	
	
	world.createNewWorld();
	
	gameClock.getTimeBegin();
	//long dtBegin = Sys.getTime();
	//renderManger.setUpWorldSize(world.getWorldSize()[0]*world.getWorldSize()[1]*world.getWorldSize()[2]);
	
	renChunk.setWorldSize(world.getWorldSize()[0]*world.getWorldSize()[1]*world.getWorldSize()[2]);
//	renderManger.addChunks(world.getManager().getChunkList());
	
	if(vboWithIndices)
	{
		renChunk.setUpWindowForIndices();
	}
	else{
	//renderManger.setUpWindow();
	renChunk.setUpWindow();
	}
	
	io.initializeKeyboard();
	if(directRender)
	{
	renChunk.initImmediateOpenGL();
	}
	
	if(vboRender)
	{
	renChunk.initVBOOpenGL();
		//
//	renderManger.setUpOpenGL();
	}
	
	renChunk.setRenderList(world.getManager().getChunkList());
	
	//initialize physics
	physics = new Physics(9.8f,world);
	physics.addBody(player);
	physics.addBody(cubeFriend);
	
	Vector<Cube> temp = new Vector<Cube>();
	temp.add(cubeFriend);
	
	renChunk.setCubeList(temp);
	//world.getManager().manageChunks(player);
	
//	renChunk.setRenderList(world.getManager().getRenderableChunks());
	
	
	if(vboRender)
	{
	renChunk.createVBOData();
//	renderManger.createChunkData();
	}
	if(vboWithIndices)
	{
		renChunk.createVBOWithIndicesData();
	}
	while(!Display.isCloseRequested())
	{
		
		
		int frames=0;
		
		while(gameClock.calcDelta()>=REDRAWING_PERIOD && frames < MAX_FRAME_SKIP)
		{
			gameClock.incrementTime(REDRAWING_PERIOD);
			
			pollForHCI((float)gameClock.getDtSeconds());
			
		//run one frame of client
			//if()
			//{
			
				itterateGameState(gameClock.getDtSeconds());
				
				gameClock.updateTime();
				
				need_to_redraw=true;
				
				frames++;
			
				
				
			
		
		}
		//compare the real clock to the actual time which has passed
		//to estimate the dt for the games next frame
		
		//dtSeconds=endTime-(double)engine.getRealClock().getTimeCycles();
		//dtSeconds=(double)engine.getRealClock().calcDeletaSeconds(engine.getGameClock());
		
		
		
		//update the real clock with the elapsed time
		//engine.getRealClock().update(dtSeconds);
		//update the game clock with the elapsed time
		//engine.getGameClock().update(dtSeconds);
		
		
		
		if(need_to_redraw)
		{
			//world.getManager().manageChunks(player);
			
			
			
			render((float)gameClock.getDtSeconds());
			//renChunk.
			
			
			need_to_redraw = false;
			Display.update();	
		}
		
	
	
		//engine.getGameClock().updateTime();
	
		
		
		
		//System.err.println("dt: "+gameClock.getDtSeconds());
		
		
		
		
	}//
	
	// save game resources
	
	//getRender().destroyOpenGl();
	renChunk.destroyOpenGlWithIndices();
	 
	
	
}


}
